#! /usr/local/bin/python
#-*- coding: utf-8 -*-


"""
pyChatter. A multi-agent based instant messaging written in Python.
Copyright (C) 2010  Cedric Bonhomme

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

pyChatter  Copyright (C) 2010  Cedric Bonhomme
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
"""

__author__ = "Cedric Bonhomme"
__version__ = "$Revision: 0.1 $"
__date__ = "$Date: 2010/01/04 $"
__copyright__ = "Copyright (c) 2010 Cedric Bonhomme"
__license__ = "GPLv3"

import os
import sys

sys.path.append('./spade/')

import spade


class ClientAgent(spade.Agent.Agent):
    """
    This class represent a client agent.
    """
    class ReceiveMessageFromAuthenticator(spade.Behaviour.Behaviour):
        """
        This class is in charge of receiving requested answer from the
        authentication agent.
        """
        def onStart(self):
            """
            """
            print "Starting", self.__class__.__name__, "behaviour"

            self.myAgent.register()

        def _process(self):
            """
            Analyse and process the anwser.
            """
            self.msg = None

            # Blocking receive indefinitely
            self.msg = self._receive(True)

            # Check wether the message arrived
            if self.msg:
                command = self.msg.getContent().split(':')
                if command[0] == "logged":
                    print "Logging in !"

                elif command[0] == "newinterlocutor":
                    self.myAgent.gui.add_client(command[1], command[2], command[3])

                elif command[0] == "already_registered":
                    self.myAgent.gui.show_info("Username already used !")

                elif command[0] == "authentication_success":
                    self.myAgent.ontology = command[1]
                    self.myAgent.launch_behaviour_client()
                    self.myAgent.gui.authentication_success()

                elif command[0] == "authentication_failed":
                    self.myAgent.gui.show_info("Authentication failed")


    class SendMessageToAuthenticator(spade.Behaviour.OneShotBehaviour):
        """
        This class is in charge of sending request to the authentication agent.
        Requests:
         - ask for registration as a new client;
         - ask for informations (ontology, public key) about an other client.
        """
        def onStart(self):
            print "Starting", self.__class__.__name__, "behaviour"

        def _process(self):
            """
            Send the request to the authentication agent.
            """
            # First, form the receiver AID
            receiver = self.myAgent.search_agent_into_df( \
                                service_type = "authentication_agent")[0].getAID()

            # Second, build the message
            self.msg = spade.ACLMessage.ACLMessage()          # Instantiate the message
            self.msg.setPerformative("inform")                # Set the "inform" FIPA performative
            self.msg.setOntology("authentication")            # Set the ontology of the message content
            self.msg.setLanguage("English")                   # Set the language of the message content
            self.msg.addReceiver(receiver)                    # Add the message receiver
            self.msg.setContent(self.myAgent.outgoing_message)# Set the message content

            # Third, send the message with the "send" method of the agent
            self.myAgent.send(self.msg)


        def onEnd(self):
            """
            """
            print "Ending", self.__class__.__name__, "behaviour"




    class ReceiveMessage(spade.Behaviour.Behaviour):
        """
        This behaviour is in charge of receiving incoming messages
        from other clients.
        """
        def onStart(self):
            """
            """
            print "Starting", self.__class__.__name__, "behaviour"


        def _process(self):
            """
            """
            self.msg = None

            # Blocking receive indefinitely
            self.msg = self._receive(True)

            # Check wether the message arrived
            if self.msg and \
                            self.msg.getSender().getName().split('@')[0] not in \
                                                self.myAgent.blocked_sender:
                self.myAgent.gui.receive_message(self.msg)


    class SendMessage(spade.Behaviour.OneShotBehaviour):
        """
        This behaviour is in charge of sending message to one or
        several agent(s).
        """
        def onStart(self):
            print "Starting", self.__class__.__name__, "behaviour"

        def _process(self):
            """
            Send the message to all recipients.
            This method search each interlocutors in the directory facilitator,
            get the public key and ontology. And send the encrypted message to
            the recipient.
            """

            for receiver_agent in self.myAgent.receiver.keys():

                # First, form the receiver AID
                receiver = self.myAgent.search_agent_into_df( \
                                    service_name = receiver_agent)[0].getAID()

                ontology = self.myAgent.receiver[receiver_agent][0]
                pubkey = self.myAgent.receiver[receiver_agent][1]

                # crypt the outgoing message with the public key of the interlocutor
                enc_message = pubkey.encrypt_text(self.myAgent.outgoing_message)

                # Second, build the message
                self.msg = spade.ACLMessage.ACLMessage()          # Instantiate the message
                self.msg.setPerformative("inform")                # Set the "inform" FIPA performative
                self.msg.setOntology(ontology)                    # Set the ontology of the message content
                self.msg.setLanguage("English")                   # Set the language of the message content
                self.msg.addReceiver(receiver)                    # Add the message receiver
                self.msg.setContent(enc_message)                  # Set the message content

                # Third, send the message with the "send" method of the agent
                self.myAgent.send(self.msg)


        def onEnd(self):
            """
            """
            print "Ending", self.__class__.__name__, "behaviour"



    def _setup(self):
        """
        Initializes the agent.
        """
        print "Starting", self.getName(), "agent"

        self.ontology = "Chat"
        self.pubkey = "pubKey"


        self.incoming_message = None
        self.outgoing_message = None
        self.receiver = {}
        self.blocked_sender = []


    def launch_behaviour_client(self):
        """
        """
        cooking_template = spade.Behaviour.ACLTemplate()
        cooking_template.setOntology(self.ontology)
        mt = spade.Behaviour.MessageTemplate(cooking_template)
        # Add the behaviour WITH the template
        receive_messabe_behaviour = self.ReceiveMessage()
        self.addBehaviour(receive_messabe_behaviour, mt)

    def launch_behaviour_authenticator(self):
        cooking_template = spade.Behaviour.ACLTemplate()
        cooking_template.setOntology("authentication")
        mt = spade.Behaviour.MessageTemplate(cooking_template)
        # Add the behaviour WITH the template
        receive_messabe_behaviour_from_authenticator = self.ReceiveMessageFromAuthenticator()
        self.addBehaviour(receive_messabe_behaviour_from_authenticator, mt)

    def tearDown(self):
        self.deregister()
        self.stop()

    def register(self):
        """
        Register the services provided by the agent.
        """
        dad = spade.DF.DfAgentDescription()

        sd = spade.DF.ServiceDescription()
        sd.setName(self.getName().split('@')[0])
        sd.setType("client_agent")
        dad.addService(sd)

        dad.setAID(self.getAID())

        try:
            result = self.registerService(dad)
        except Exception, e:
            print "Registration of", self.getName(), "failed"

    def deregister(self):
        """
        Unregister the agent from the Directory Facilitator.
        """
        dad = spade.DF.DfAgentDescription()
        dad.setAID(self.getAID())
        try:
            self.deregisterService(dad)
        except Exception, e:
            print "Deregistration of", self.getName(), "failed"

    def search_agent_into_df(self, service_name = None, service_type = None):
        """
        Search for a client agent according to its service name or
        service type.
        """
        dad = spade.DF.DfAgentDescription()
        ds = spade.DF.ServiceDescription()
        if service_type == None:
            ds.setType("client_agent")
            ds.setName(service_name)
        else:
            ds.setType(service_type)
        dad.addService(ds)

        return self.searchService(dad)


if __name__ == "__main__":
    # Point of entry in execution mode
    from optparse import OptionParser
    parser = OptionParser()
    parser.add_option("-n", "--agent-name", dest="agent_name",
                    help="Name of the client agent")
    parser.add_option("-s", "--agent-host", dest="agent_host",
                    help="Address of the SPADE platform")
    parser.set_defaults(agent_name = 'bob',
                    agent_host = '127.0.0.1')

    (options, args) = parser.parse_args()


    client_agent = ClientAgent(options.agent_name + "@" + options.agent_host, \
                                "secret")
    client_agent.start()